package com.amadeus.spin.standard.util;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.amadeus.spin.devtool.soap.shooter.FatalException;

/**
 * Util sutff has to be refactored.. or even removed.
 */
public abstract class EncodingUtils {
  private static final String ENCODING = "encoding";

  private static final Pattern ENCODING_BE_PATTERN = Pattern.compile(ENCODING + "=[\"']([^\"']*)[\"']");

  private final static int[][] BYTE_MASKS = {
      // UTF - 8
      { 0xEF, 0xBB, 0xBF },
      // UTF - 16, big - endian
      { 0xFE, 0xFF },
      // UTF - 16, little - endian
      { 0xFF, 0xFE },
      // UTF - 32, big - endian
      { 0x00, 0x00, 0xFE, 0xFF },
      // UTF - 32, little - endian
      { 0xFF, 0xFE, 0x00, 0x00 } };

  private final static String[] BYTE_MASKS_ENCODINGS = { "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE" };

  private final static String[] POSSIBLE_ENCODINGS = { "UTF-8", "ISO-8859-1", "US-ASCII", "UTF-16", "UTF-16BE",
      "UTF-16LE" };

  /**
   * return the encoding of the given file, null if not found
   *
   * @throws FatalException
   * @throws IOException
   */
  public static String getFileEncoding(String filepath) throws IOException {
    boolean encodingFound = false;
    String encodingUsed = null;

    // Try to read opening xml tag using all supported encoding
    int possibleEncodingsIndex = 0;
    do {
      InputStreamReader isr;
      BufferedReader br;
      FileInputStream inp = new FileInputStream(filepath);
      isr = new InputStreamReader(inp, POSSIBLE_ENCODINGS[possibleEncodingsIndex++]);
      br = new BufferedReader(isr);
      try {
        String xmlDeclaration = br.readLine();
        if (xmlDeclaration != null && xmlDeclaration.startsWith("<?xml")) {
          encodingFound = true;
          encodingUsed = POSSIBLE_ENCODINGS[possibleEncodingsIndex - 1];
          break;
        }
      }
      finally {
        br.close();
        inp.close();
      }
    }
    while (possibleEncodingsIndex < POSSIBLE_ENCODINGS.length);

    if (encodingFound) {
      return encodingUsed;
    }
    // Try to get input file's encoding by checking its first bytes
    byte[] bom = new byte[4];
    FileInputStream fileInputStream = new FileInputStream(filepath);
    try {
      fileInputStream.read(bom, 0, bom.length);
    }
    finally {
      fileInputStream.close();
    }
    for (int i = 0; i < BYTE_MASKS.length && !encodingFound; i++) {
      encodingFound = true;
      for (int k = 0; k < BYTE_MASKS[i].length && encodingFound; k++) {
        if (BYTE_MASKS[i][k] != (bom[k] & 0xFF)) {
          encodingFound = false;
        }
      }
      if (encodingFound) {
        encodingUsed = BYTE_MASKS_ENCODINGS[i];
      }
    }

    // If no encoding could be deduced from file's top bytes, try to get input
    // file's encoding from XML declaration
    if (!encodingFound) {
      encodingUsed = readEncodingFromXml(filepath);
    }
    return encodingUsed;
  }

  /**
   * reads the encoding from the xml
   * 
   * @param filepath
   * @param encodingUsed
   * @return
   * @throws FileNotFoundException
   * @throws IOException
   * @throws UnsupportedEncodingException
   */
  protected static String readEncodingFromXml(String filepath) throws FileNotFoundException,
      IOException, UnsupportedEncodingException {
    String encodingUsed = null;
    int possibleEncodingsIndex;
    possibleEncodingsIndex = 0;
    // All we want is to read XML declaration tag, so we first try to open
    // file using OS default encoding
    BufferedReader br;
    FileReader fileReader = new FileReader(filepath);
    br = new BufferedReader(fileReader);
    try {
      do {
        String xmlDeclaration = br.readLine();

        // We jump to XML tag if not on first line
        while (xmlDeclaration != null && !xmlDeclaration.contains("<?xml")) {
          xmlDeclaration = br.readLine();
        }

        // If we found an XML tag
        if (xmlDeclaration != null) {
          // Then we look for encoding attribute
          int encodingIndex = xmlDeclaration.indexOf(ENCODING);

          // If an encoding is specified we will use it
          if (encodingIndex != -1) {
            encodingIndex = xmlDeclaration.indexOf("\"", encodingIndex);
            encodingUsed = xmlDeclaration.substring(encodingIndex + 1, xmlDeclaration.indexOf("\"", encodingIndex + 1));

            // If a valid encoding is found we stop here
            if (encodingUsed != null && encodingUsed.length() > 0) {
              break;
            }
          }
        }
        // Else we try openning file using another encoding
        InputStreamReader isr = new InputStreamReader(new FileInputStream(
            filepath), POSSIBLE_ENCODINGS[possibleEncodingsIndex++]);
        br = new BufferedReader(isr);
      }
      while (possibleEncodingsIndex < POSSIBLE_ENCODINGS.length);
    }
    finally {
      br.close();
      fileReader.close();
    }
    return encodingUsed;
  }

  /**
   * reads encoding of responses of the type <?xml version=\"1.0\" encoding=\"iso-8859-1\"?>
   * 
   * @param xmlResponse
   * @return
   */
  public static String getStringEncoding(String xmlResponse) {
    String respEncoding = null;
    Matcher matcher = ENCODING_BE_PATTERN.matcher(xmlResponse);
    if (matcher.find()) {
      respEncoding = matcher.group(1);
    }
    return respEncoding;
  }
}